package cate.game.friend;

import cate.common.table.d.GDFriend;
import cate.common.table.d.PerformanceParams;
import cate.common.util.GameResult;
import cate.common.util.XT;
import cate.game.db.SaveConstants;
import cate.game.framework.ModuleMan;
import cate.game.framework.ThreadShare;
import cate.game.friend.po.FriendBox;
import cate.game.role.Role;
import cate.game.role.RoleSnapshot;
import cate.game.role.friend.Friend;
import cate.game.role.friend.FriendApplyItem;
import cate.game.role.friend.FriendGiftItem;
import cate.game.role.friend.msg.FriendRecommendResp;
import cate.game.util.IntervalCounter;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

/** 好友模块管理 */
public class FriendManager extends ModuleMan {
    private final Map<String, FriendBox> boxCache = new HashMap<>();

    /** 推荐玩家池 */
    private final List<RoleSnapshot> recommendPool = new CopyOnWriteArrayList<>();

    /** 上次更新推荐玩家吃的时间 */
    private long lastUpdateRecommendTime = -1;

    @Override
    public void initDependent() throws Exception {

    }

    /** 获取离线收件箱 */
    public FriendBox getBox(String roleUid, boolean required) {
        FriendBox box = boxCache.get(roleUid);
        if (box == null) {
            synchronized (this){
                box = boxCache.get(roleUid);
                if (box == null) {
                    FriendBox dbBox = game.db.friendBox.find(roleUid);
                    if (dbBox != null) {
                        dbBox.initialize();
                        boxCache.put(roleUid, dbBox);
                    }else if(required){
                        dbBox = new FriendBox();
                        dbBox.initialize();
                        dbBox.uid = roleUid;
                        boxCache.put(roleUid, dbBox);
                    }
                    box = dbBox;
                }
            }
        }
        return box;
    }

    public boolean canAdd(Friend friend){
        Role role = game.role.getRole(friend.ownerIdHex,true);
        return !role.getFriend().inBlackList(friend.it.uid);
    }

    /** 更新推荐玩家池 */
    private void updateRecommendPool() {
        long now = System.currentTimeMillis();
        if (now - lastUpdateRecommendTime
                > PerformanceParams.FRIEND_RECOMMEND_POOL_UPDATE_INTERVAL) {
            recommendPool.clear();
            game.role.activeForOnlineSelfThread(
                    m -> recommendPool.add(new RoleSnapshot(m)));
            lastUpdateRecommendTime = now;
        }
    }

    /** 向玩家推荐好友 */
    public void recommend(Role role) {
        updateRecommendPool();
        List<RoleSnapshot> rl = new ArrayList<>();
        synchronized (recommendPool) {
            final int wantNum = GDFriend.SEARCH_MAX;
            if (!recommendPool.isEmpty()) {
                int maxTry = wantNum + 20;
                Set<String> idHexSet = new HashSet<>();
                while (--maxTry >= 0) {
                    int pos = XT.randomIndex(recommendPool.size());
                    RoleSnapshot other = recommendPool.get(pos);
                    if (other == null) {
                        log.error("好友推荐时遇到other==null, pos={}, recommendPool.size()={}", pos, recommendPool.size());
                        continue;
                    }
                    if (idHexSet.contains(other.uid)) {
                        continue;
                    } else {
                        idHexSet.add(other.uid);
                    }
                    if (StringUtils.equals(role.getUid(), other.uid)) {
                        continue; // 不推荐自己
                    }
                    if (null != role.getFriend().getFriend(other.uid)) {
                        continue; // 不推荐已有的好友
                    }
                    if (role.getFriend().inBlackList(other.uid)){
                        continue; //黑名单
                    }
                    rl.add(other);
                    if (rl.size() >= wantNum) {
                        break;
                    }
                }
            }
        }
        role.sendNow(new FriendRecommendResp(rl));
    }

    /** 收到消息队列提供的好友申请 */
    public GameResult<Void> recvApply(Role role, FriendApplyItem applyItem) {
        GameResult<Void> r = new GameResult<>();
        if (role.getFriend().inBlackList(applyItem.applicant.uid)) {
            return r.fail("对方已经将您拉黑，无法添加其为好友");
        }
        r = role.getFriend().recv(applyItem);
        return r;
    }

    /** 收到消息队列提供的收到好友礼物 */
    public void recvGift(FriendGiftItem giftItem) {
        if (!game.role.activeForOnlineRole(giftItem.targetIdHex, e -> e.getFriend().recv(giftItem))) {
            getBox(giftItem.targetIdHex, true).add(giftItem);
        }
    }

    /** 收到消息队列提供的好友删除(被删除) */
    public void deleted(String whoIdHex, String tarIdHex, boolean removeApplication) {
        Role role = game.role.getRole(whoIdHex);
        if(role != null){
            ThreadShare.instance().exe(role, () -> {
                role.getFriend().delete(tarIdHex, removeApplication);
            });
        }
    }

    @Override
    public synchronized void shutdown(){
        for (FriendBox box : boxCache.values()) {
            if (box.isEmpty()) {
                game.db.friendBox.delete(box.uid);
            } else {
                game.db.friendBox.upsert(box);
            }
        }
        boxCache.clear();
    }

    private static final AtomicInteger IDX = new AtomicInteger(0);
    private final IntervalCounter saveTrigger = new IntervalCounter(SaveConstants.FRIEND_SAVE_SECONDS, IDX.getAndIncrement());
    public void tick(){
        if(saveTrigger.trigger()){
            long start = System.currentTimeMillis();
            shutdownSafe();
            log.debug("game friend定时保存完成, cost{} ms", System.currentTimeMillis() - start);
        }
    }
}